package cgroup

import (
	"JudgeSandBox/common"
	"JudgeSandBox/utils"
	"fmt"
	"os"
	"path"
	"strconv"
)

type Memory struct {
	subsystem
	uuid string
	path string
}

func GetMemory(uuid string) *Memory {
	sprintf := fmt.Sprintf(common.CgroupMemoryPath, uuid)
	if err := os.MkdirAll(sprintf, 0755); err != nil {
		utils.PrintToLog("Error creating Memory object:", err)
		return nil
	}
	return &Memory{
		uuid: uuid,
		path: sprintf,
	}
}

func (m Memory) GetUUID() string {
	return m.uuid
}

func (m Memory) getPath() string {
	return m.path
}

// Set 设置最大内存限制
func (m Memory) Set(maxMemoryLimit string) error {
	if err := os.WriteFile(path.Join(m.path, common.MemoryLimitInBytes), []byte(maxMemoryLimit), 0755); err != nil {
		return err
	}
	//memory.swappiness中的数值其实并不是确切的数值，而是代表了进程使用swap空间的一个权重，该数值范围从0-100。100表示积极使用swap，0表示优先使用内存。
	if err := os.WriteFile(path.Join(m.path, common.MemorySwapiness), []byte(strconv.Itoa(0)), 0755); err != nil {
		return err
	}
	// oom kill
	if err := os.WriteFile(path.Join(m.path, common.MemoryOOMControl), []byte(strconv.Itoa(0)), 0755); err != nil {
		return err
	}
	return nil
}

func (m Memory) Put(pid int) error {
	if err := os.WriteFile(path.Join(m.path, common.Tasks), []byte(strconv.Itoa(pid)), 0755); err != nil {
		return err
	}
	return nil
}

func (m Memory) Remove() error {
	return os.RemoveAll(m.path)
}

// OOMCheck 在进程出错时，用来判断是否因为oom而被杀掉，只有 OOMCheck 方法不返回 error 时才是正确的结果
func OOMCheck(uuid string) (bool, error) {
	mPath := fmt.Sprintf(common.CgroupMemoryPath, uuid)
	max_usage_in_bytes, max_err := os.ReadFile(path.Join(mPath, common.MemoryMaxUsageInBytes))
	if max_err != nil {
		return false, max_err
	}
	limit_in_bytes, limit_err := os.ReadFile(path.Join(mPath, common.MemoryLimitInBytes))
	if limit_err != nil {
		return false, limit_err
	}

	max, maxErr := strconv.ParseInt(utils.Trim(string(max_usage_in_bytes)), 10, 64)
	limit, limitErr := strconv.ParseInt(utils.Trim(string(limit_in_bytes)), 10, 64)
	if maxErr != nil {
		return false, maxErr
	}
	if limitErr != nil {
		return false, limitErr
	}
	return max >= limit, nil
}

func GetMaxMemory(uuid string) (int64, error) {
	mPath := fmt.Sprintf(common.CgroupMemoryPath, uuid)
	maxUsageInBytes, maxErr := os.ReadFile(path.Join(mPath, common.MemoryMaxUsageInBytes))
	if maxErr != nil {
		return -1, maxErr
	}
	if parseInt, err := strconv.ParseInt(utils.Trim(string(maxUsageInBytes)), 10, 64); err != nil {
		return -1, err
	} else {
		return parseInt, nil
	}
}
